#ifndef TRIBOOL_HPP
# define TRIBOOL_HPP

#include <iostream>

enum TriboolVal
{
    True,
    False,
    Undefined,
    OutOrStopped
};

struct Tribool
{
    Tribool() : value(TriboolVal::Undefined) {}
    Tribool(Tribool const & cp) : value(cp.value) {}
    Tribool(TriboolVal p_value) : value(p_value) {}
    Tribool(bool cp) { value = cp ? True : False; }
    Tribool& operator=(Tribool const & cp) { this->value = cp.value; return *this; }
    virtual ~Tribool() {}

    Tribool operator==(Tribool const & cmp) { return this->value == cmp.value; }

    Tribool operator&&(Tribool const & cmp)
    {
        if (this->value == TriboolVal::OutOrStopped || cmp.value == TriboolVal::OutOrStopped)
            return TriboolVal::OutOrStopped;
        if (this->value == TriboolVal::False || cmp.value == TriboolVal::False)
            return TriboolVal::False;
        if (this->value == TriboolVal::Undefined || cmp.value == TriboolVal::Undefined)
            return TriboolVal::Undefined;
        return TriboolVal::True;
    }

    Tribool operator||(Tribool const & right)
    {
        if (this->value == TriboolVal::OutOrStopped || right.value == TriboolVal::OutOrStopped)
            return TriboolVal::OutOrStopped;
        if (this->value == TriboolVal::True || right.value == TriboolVal::True)
            return TriboolVal::True;
        if (this->value == TriboolVal::Undefined || right.value == TriboolVal::Undefined)
            return TriboolVal::Undefined;
        return TriboolVal::False;
    }

    Tribool operator^(Tribool const & right)
    {
        if (this->value == TriboolVal::OutOrStopped || right.value == TriboolVal::OutOrStopped)
            return TriboolVal::OutOrStopped;
        if (this->value == TriboolVal::Undefined || right.value == TriboolVal::Undefined)
            return TriboolVal::Undefined;
        if ((this->value == TriboolVal::True && right.value == TriboolVal::False)
            || (right.value == TriboolVal::True && this->value == TriboolVal::False))
            return TriboolVal::True;
        return TriboolVal::False;
    }

    Tribool operator!()
    {
        if (this->value == TriboolVal::OutOrStopped)
            return TriboolVal::OutOrStopped;
        if (this->value == TriboolVal::Undefined)
            return TriboolVal::Undefined;
        if (this->value == TriboolVal::True)
            return TriboolVal::False;
        return TriboolVal::True;
    }

    TriboolVal value;
};

inline std::ostream & operator<<(std::ostream& s, Tribool const & tb)
{
    if (tb.value == True)
        s << "True";
    else if (tb.value == False)
        s << "False";
    else if (tb.value == OutOrStopped)
        s << "Stopped by out-or node";
    else
        s << "Undefined";
    return s;
}

#endif // TRIBOOL_HPP
